package com.starblink.utils.network.base

import android.util.Log
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.api.ApolloResponse
import com.apollographql.apollo3.api.Mutation
import com.apollographql.apollo3.api.Query
import com.apollographql.apollo3.cache.normalized.FetchPolicy
import com.apollographql.apollo3.cache.normalized.fetchPolicy
import com.apollographql.apollo3.cache.normalized.normalizedCache
import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory
import com.apollographql.apollo3.interceptor.ApolloInterceptor
import com.apollographql.apollo3.network.http.HttpNetworkTransport
import com.apollographql.apollo3.network.okHttpClient
import com.starblink.utils.network.config.HeadsHandle
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectIndexed
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import java.util.concurrent.TimeUnit


open class SKGqlClient {
    private var isDebug = false // 是否是调试模式
    private var baseUrl = "" // 请求地址
    private var apolloClient: ApolloClient? = null
    private var interceptors: List<ApolloInterceptor>? =null

    //https://www.apollographql.com/docs/kotlin/caching/normalized-cache/
    private val sqlNormalizedCacheFactory = SqlNormalizedCacheFactory("utils-apollo.db")

    //初始化请求 后续修改可设置其他参数
    fun init(baseUrl:String,
             interceptors: List<ApolloInterceptor>?=null,
             isDebug: Boolean=false) {
        this.baseUrl = baseUrl
        this.isDebug = isDebug
        this.interceptors=interceptors
        getClient()
    }

    fun getClient(): ApolloClient {
        if (apolloClient == null) {
            val okHttpClient =OkHttpClient.Builder()
                .callTimeout(10, TimeUnit.SECONDS)
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true)
                .followRedirects(false)
                .addInterceptor(HttpLoggingInterceptor().apply {
                    setLevel(if (isDebug) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE)
                })
                .build()

            apolloClient = ApolloClient.Builder()
                //添加公共请求头
                .httpHeaders(HeadsHandle.getHttpDfHeads(false))
                .normalizedCache(sqlNormalizedCacheFactory)
                .addInterceptors(interceptors?: listOf())
                .networkTransport(
                    HttpNetworkTransport.Builder()
                        .okHttpClient(okHttpClient)
                        .httpRequestComposer(
                            SkApolloRequestComposer(baseUrl)
                        ).build()
                )
                .build()
        }

        return apolloClient!!

    }

    companion object {
        private val TAG = SKGqlClient::class.java.simpleName
        private var instance: SKGqlClient? = null
        fun getInstance(): SKGqlClient {
            if (instance == null) {
                instance = SKGqlClient()
            }
            return instance!!
        }

        suspend fun <D : Mutation.Data> skMutation(
            mutation: Mutation<D>,
            //mutation请求默认总是使用网络！！！
            fetchPolicy: FetchPolicy = FetchPolicy.NetworkOnly,
        ): ApolloResponse<D>? {
            return try {
                val apolloClient = getInstance().getClient()
                apolloClient
                    .mutation(mutation)
                    .fetchPolicy(fetchPolicy)
                    .execute()

            } catch (exception: Exception) {
                Log.e(TAG, "${exception.message}")
                null
            }

        }

        suspend fun <D : Query.Data> skQueryCacheAndNetwork(
            query: Query<D>,
        ): ApolloResponse<D>? {
            var result : ApolloResponse<D>?= null
            return try {
                val apolloClient = getInstance().getClient()
                apolloClient.query(query)
                    .fetchPolicy(FetchPolicy.CacheAndNetwork)
                    .toFlow().filterNotNull().collect {  response ->
                        Log.d(TAG, "---- 有网络时 会执行两次 一次缓存 一次网络 ----")
                        result = response
                    }
                //CacheAndNetwork 策略可以发出多个值，因此您调用 toFlow() 而不是 execute()
                result
            } catch (exception: Exception) {
                Log.e(TAG, "${exception.message}")
                //网络异常时 会走缓存
                result
            }
        }

        suspend fun <D : Query.Data> skQuery(
            query: Query<D>,
            fetchPolicy: FetchPolicy = FetchPolicy.NetworkOnly,
        ): ApolloResponse<D>? {
            return try {
                val apolloClient = getInstance().getClient()
                apolloClient.query(query)
                    .fetchPolicy(fetchPolicy)
                    .execute()
            } catch (exception: Exception) {
                Log.e(TAG, "${exception.message}")
                null
            }
        }
    }
}